package net.vsame.url2sql.web;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.vsame.url2sql.domain.Url2SqlContext;
import net.vsame.url2sql.domain.Url2SqlModel;
import net.vsame.url2sql.domain.UrlConfig;
import net.vsame.url2sql.helper.Url2SqlMappingHelper;
import net.vsame.url2sql.helper.WebHelper;
import net.vsame.url2sql.utils.JarUtils;
import net.vsame.url2sql.web.bigpipe.BigPipeHelper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class Url2SqlFilter implements Filter {
	
	private static Log LOG = LogFactory.getLog(Url2SqlFilter.class);
	private static String realPath;
	private static String encoding = "utf-8";
	private static boolean enabledJsonp = true;
	private static Charset charset = null;

    public Url2SqlFilter() {
    }

	public void destroy() {
	}

	public void doFilter(ServletRequest _req, ServletResponse _resp, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) _req;
		HttpServletResponse response = (HttpServletResponse) _resp;
		// 设置编码
		request.setCharacterEncoding(encoding);
		response.setCharacterEncoding(encoding);
		try{
			WebHelper.init(request, response);
			boolean[] isHandled = new boolean[]{false};
			handel(request.getRequestURI().substring(request.getContextPath().length()), request, response, isHandled);
			
			if(!isHandled[0]){
				//WebHelper中的request, response可以包装 
				chain.doFilter(WebHelper.getContext().getRequest(), WebHelper.getContext().getResponse());
			}
		}finally{
			WebHelper.remove();
		}
	}
	
	private Url2SqlContext getContext(String requestURI){
		
		String[] urlNodes = requestURI.split("/");
		if(urlNodes.length==0){
			return null;
		}
		String last = urlNodes[urlNodes.length-1];
		
		Url2SqlContext context = WebHelper.getContext();
		if(last.endsWith(".json")){
			//JSON
			context.setExtensionName(".json");
			context.setHandleType(HandleType.JSON);
		}
		else if(last.endsWith(".jsonp")){
			//JSONP
			context.setExtensionName(".jsonp");
			context.setHandleType(HandleType.JSONP);
		}
		else if(!last.contains(".")){
			//Bigpipe
			context.setExtensionName("");
			context.setHandleType(HandleType.BIGPIPE);
		}
		else {
			context.setExtensionName("");
			context.setHandleType(HandleType.RESOURCE);
		}
		
		//判断在哪个Jar包
		if(urlNodes.length>=2){
			context.setUrl2SqlModel(Url2SqlMappingHelper.getModel(urlNodes[1]));
		}
		
		//取得对应的Action
		String rest = null;
		String url = null;
		String name = last.substring(0, last.length()-context.getExtensionName().length());
		int i = name.indexOf("_");
		if(i != -1){
			rest = name.substring(i+1);
			url = requestURI.substring(0, requestURI.length() - last.length() + i);
		}else{
			url = requestURI.substring(0, requestURI.length() - context.getExtensionName().length());
		}
		
		UrlConfig c = Url2SqlMappingHelper.getConfig(url);
		if(c!=null){
			context.setRequestUrl(url);
			context.setUrlConfig(c);
			context.setRestfulParam(rest);
		}
		return context;
	}
	
	/**
	 * 
	 * @param requestURI
	 * @param request
	 * @param response
	 * @param context
	 * @param isHandled
	 * @return true 表示是资源，已经处理
	 */
	private void handelResource(String requestURI, HttpServletRequest request, HttpServletResponse response, 
			Url2SqlContext context, boolean[] isHandled){
		Url2SqlModel model = context.getUrl2SqlModel();
		
		//类目录下，利用DefaultServlet实现缓存
		if(model == null){
			return ;
		}
		
		//输出Jar包资源
		InputStream in = JarUtils.getResourceAsStreamFromJar(model.getClass(), "WebRoot" + requestURI.substring(model.getContextPath().length()+1));
		if(in == null){//扔出404
			return ;
		}
		
		isHandled[0] = true;
		OutputStream out = null;
		try {
			out = response.getOutputStream();
			
			byte[] b = new byte[1024 * 5];
            int len;
            while ((len = in.read(b)) != -1) {
                out.write(b, 0, len);
            }
			
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				try{
					in.close();
				}finally{
					out.flush();
					out.close();
				}
			} catch (IOException e) {
			}
		}
		return ;
	}
	
	private PrintWriter getOut(HttpServletResponse response){
		try {
			return response.getWriter();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	private void handel(String requestURI, HttpServletRequest request,
			HttpServletResponse response, boolean[] isHandled) {
		
		Url2SqlContext context = getContext(requestURI);
		if(context == null){
			return ;
		}
		
		HandleType type = context.getHandleType();
		
		if(type == HandleType.RESOURCE){
			//资源
			handelResource(requestURI, request, response, context, isHandled);
			return ;
		}
		
		//视图
		isHandled[0] = true;
		PrintWriter out = null;
		
		switch (type) {
		case JSON:
			out = getOut(response);
			response.setContentType("application/x-javascript; charset="+encoding);
			out.print(getJosn(context));
			break;
			
		case JSONP:
			out = getOut(response);
			response.setContentType("application/x-javascript; charset="+encoding);
			out.print(getJosnp(context));
			break;
			
		case BIGPIPE:
			int i = requestURI.indexOf("_");
			String fileName = requestURI;
			if(i != -1){
				fileName = requestURI.substring(0, i);
			}
			
			URL url = null;
			File f = new File(realPath + fileName + ".html");
			if(f.exists()){
				try {
					url = f.toURI().toURL();
				} catch (MalformedURLException e) {
					throw new RuntimeException(e);
				}
			}else{
				if(context.getUrl2SqlModel() == null){
					isHandled[0] = false;
					return ;//抛出 404 去吧
				}
				Class<? extends Url2SqlModel> clazz = context.getUrl2SqlModel().getClass();
				url = JarUtils.getResourceFromJar(clazz, "WebRoot" + fileName.substring(fileName.indexOf("/", 1)) + ".html");
			}
			
			if(url == null){
				return ;//抛出 404 去吧
			}
			
			
			String parent = requestURI.substring(0, requestURI.lastIndexOf("/"));
			
			out = getOut(response);
			out.println(BigPipeHelper.readHtml(url, parent, true));
			out.println(BigPipeHelper.getCommScript(request, response));
			out.flush();
			
			out.println("<script type=\"text/javascript\">");
			out.println(getJosnp(context));
			out.print("</script></html>");
			break;
			
		default:
			isHandled[0] = false;
			break;
		}
		
		out.flush();
	}

	private String getJosnp(Url2SqlContext context) {
		String method = context.getRequest().getParameter("callback");
		method = method == null ? "callback" : method;
		String retVal = null;
		if(enabledJsonp){
			retVal = ";" + method + "(" + getJosn(context) + ");";
		}else{
			retVal = ";" + method + "(" + "{'code':'-7', 'msg':'disabled jsonp'}" + ");";
		}
		return retVal;
	}

	private String getJosn(Url2SqlContext context) {
		UrlConfig c = context.getUrlConfig();
		if(context.hasError()){
			//已包含错误
		}else if(c == null){
			//API不存在
			context.put404();
		}else if(c.getStatus()!=1){
			context.putError(-7, "disabled");
		}else {
			//调用
			try{
				Url2SqlCore.handel();
			}catch(Exception e){
				context.error(e, null);
				try {
					context.getConn().rollback();//回滚
				} catch (Exception e1) {
				}
			}
		}
		return JSON.toJSONString(context.getDatas(), SerializerFeature.WriteDateUseDateFormat);
	}

	public void init(FilterConfig config) throws ServletException {
		realPath = config.getServletContext().getRealPath("");
		encoding = config.getInitParameter("encoding");
		// 默认编码为UTF-8
		if (encoding == null || "".equals(encoding)) {
			encoding = "UTF-8";
		}
		String jsonp = config.getInitParameter("jsonp");
		if("false".equalsIgnoreCase(jsonp)){
			enabledJsonp = false;
		}
		charset = Charset.forName(encoding);
		LOG.info("url2sql core init. encoding:" + encoding + ". jsonp="+enabledJsonp);
		
		//载入jar包
		Url2SqlMappingHelper.reload(); 
		
		/*
		UrlConfig urlConfig = new UrlConfig();
		urlConfig.setUrl("/edit/s");
		urlConfig.setJs("this.put('a', 'gaollg');");
		Url2SqlMappingHelper.addUrlConfig(urlConfig );
		
		urlConfig = new UrlConfig();
		urlConfig.setUrl("/h");
		urlConfig.setJs("var m = SQL.query('select * from student');this.put('m', m);");
		Url2SqlMappingHelper.addUrlConfig(urlConfig );
		
		urlConfig = new UrlConfig();
		urlConfig.setUrl("/i");
		urlConfig.setJs("var m = SQL.execute('INSERT INTO `student` (`name`, `age`) VALUES (${name}, ${0})');this.put('m', m);");
		Url2SqlMappingHelper.addUrlConfig(urlConfig );
		
		urlConfig = new UrlConfig();
		urlConfig.setUrl("/s");
		urlConfig.setJs("var m = SQL.query('select * from student where id in (${0})');this.put('m', m);");
		Url2SqlMappingHelper.addUrlConfig(urlConfig );
		*/
	}
	
	public static String getEncoding() {
		return encoding;
	}
	public static Charset getCharset() {
		return charset;
	}
	public static String getRealPath() {
		return realPath;
	}
}
